package com.cq.hd.admin.controller;

import com.cq.hd.admin.config.LockUtils;
import com.cq.hd.admin.context.ApplicationContextHolder;
import com.cq.hd.admin.dto.LoginUserDto;
import com.cq.hd.admin.dto.MobileLoginUserDto;
import com.cq.hd.admin.po.TbAdminUserPo;
import com.cq.hd.admin.service.TbAdminPermissionService;
import com.cq.hd.admin.service.TbAdminRoleService;
import com.cq.hd.admin.service.TbAdminUserService;
import com.cq.hd.admin.utils.PermissionUtil;
import com.cq.hd.admin.utils.Permission;
import com.cq.hd.admin.utils.StringUtil;
import com.cq.hd.common.constant.Constant;
import com.cq.hd.common.constant.RedisKeyConstant;
import com.cq.hd.common.enums.biz.SmsTypeEnum;
import com.cq.hd.common.response.ResultGenerator;
import com.cq.hd.common.utils.*;
import com.cq.hd.member.api.SmsApi;
import com.cq.hd.member.api.dto.SmsVerifyDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Api(tags = "权限认证相关接口")
@Slf4j
@RestController
@RequestMapping("/auth")
public class AuthController extends BaseController {

    @Autowired
    private TbAdminUserService adminUserService;

    @Autowired
    private TbAdminRoleService adminRoleService;

    @Autowired
    private TbAdminPermissionService adminPermissionService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private SmsApi smsApi;

    private HashMap<String, String> systemPermissionsMap = null;

    public static void main(String[] args) {
        String userName = "厦门火动通告投资合伙企业（有限合伙）";
        String password = "123456";
        String encodedPassword = StringUtil.SHA1(userName + password);
        System.out.println(encodedPassword);
    }

    @ApiOperation(value = "用户名密码登录接口")
    @PostMapping("/login")
    public Object login(@RequestBody LoginUserDto loginUserDto, HttpServletRequest request) {
        if (loginUserDto == null || StringUtils.isEmpty(loginUserDto.getUserName()) || StringUtils.isEmpty(loginUserDto.getPassword())) {
            return ResultGenerator.error("登录失败");
        }
        String userName = loginUserDto.getUserName();
        String password = loginUserDto.getPassword();
        if (StringUtils.isEmpty(userName)) {
            return ResultGenerator.error("用户名不能为空");
        }
        if (StringUtils.isEmpty(password)) {
            return ResultGenerator.error("密码不能为空");
        }
        TbAdminUserPo user = adminUserService.getUserByUserName(userName);
        if (user == null) {
            return ResultGenerator.error("该用户不存在");
        }

        String encodedPassword = StringUtil.SHA1(userName + password);
        if (StringUtils.isEmpty(encodedPassword) || !(encodedPassword.equals(user.getPassword()))) {
            return ResultGenerator.error("用户名或密码错误");
        }
        if (user.getStatus() == null || user.getStatus() != 0) {
            return ResultGenerator.error("您已被禁用");
        }

        user.setLastLoginIp(IpUtil.getIpAddr(request));
        user.setLastLoginTime(LocalDateUtil.getNowLocalDateTime());
        adminUserService.updateById(user);

        log.info("用户" + loginUserDto.getUserName() + "账号密码方式登录系统");
        String userId = String.valueOf(user.getId());
        // 生成随机密钥
        String logKey = GenerateUtil.getRandomNum(8);
        // 生成token
        String token = JwtUtil.createToken(logKey, userId);
        redisUtil.set(String.format(RedisKeyConstant.ADMIN_USER_LOGIN_KEY, userId), logKey, Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);
        return ResultGenerator.success(token);
    }

    @ApiOperation(value = "手机短信验证码登录接口")
    @PostMapping("/mobileLogin")
    public Object mobileLogin(@RequestBody MobileLoginUserDto mobileLoginUserDto, HttpServletRequest request) {
        String mobile = mobileLoginUserDto.getMobile();
        String smsCode = mobileLoginUserDto.getSmsCode();
        if (StringUtils.isEmpty(mobile)) {
            return ResultGenerator.error("请输入手机号");
        }

        if (StringUtils.isEmpty(smsCode)) {
            return ResultGenerator.error("请输入验证码");
        }

        TbAdminUserPo user = adminUserService.getUserByMobile(mobile);
        if (user == null) {
            return ResultGenerator.error("手机号错误");
        }

        if (user.getStatus() == null || user.getStatus() != 0) {
            return ResultGenerator.error("您已被禁用");
        }

        // 对手机号加锁
        String lockKey = MessageFormat.format(Constant.LOCK_FORMAT, "adminLoginByMobile:" + mobile);
        try {
            LockUtils.blockLock(lockKey, Constant.AUTO_UNLOCK_SECOND);

            // 判断短信验证码是否正确
            SmsVerifyDto smsVerifyDto = new SmsVerifyDto();
            smsVerifyDto.setCode(smsCode);
            smsVerifyDto.setMobile(mobile);
            smsVerifyDto.setType(SmsTypeEnum.SMS_ADMIN_BUSINESS_LOGIN.getValue());
            smsApi.verify(smsVerifyDto).isSuccess();

            user.setLastLoginIp(IpUtil.getIpAddr(request));
            user.setLastLoginTime(LocalDateUtil.getNowLocalDateTime());
            adminUserService.updateById(user);
            log.info("用户" + user.getUserName() + "短信验证码方式登录系统");

            String userId = String.valueOf(user.getId());
            // 生成随机密钥
            String logKey = GenerateUtil.getRandomNum(8);
            // 生成token
            String token = JwtUtil.createToken(logKey, userId);
            redisUtil.set(String.format(RedisKeyConstant.ADMIN_USER_LOGIN_KEY, userId), logKey, Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);
            return ResultGenerator.success(token);
        } finally {
            LockUtils.unlock(lockKey);
        }
    }


//    @ApiOperation(value = "登录接口")
//    @PostMapping("/login")
//    public Object login(@RequestBody LoginUserDto loginUserDto, HttpServletRequest request) {
//
//        try {
//            if (loginUserDto == null || StringUtils.isEmpty(loginUserDto.getUserName()) || StringUtils.isEmpty(loginUserDto.getPassword())) {
//                return ResultGenerator.error("参数异常！");
//            }
//            try {
//                SecurityUtils.getSubject().login(new UsernamePasswordToken(loginUserDto.getUserName(), loginUserDto.getPassword()));
//            } catch (UnknownAccountException uae) {
//                log.error("登录失败！", uae);
//                return ResultGenerator.error(ADMIN_INVALID_ACCOUNT, uae.getMessage());
//            } catch (LockedAccountException lae) {
//                log.error("用户帐号已锁定不可用！", lae);
//                return ResultGenerator.error(ADMIN_INVALID_ACCOUNT, "用户帐号已锁定不可用！");
//            } catch (AccountException ae) {
//                log.error(ae.getMessage());
//                return ResultGenerator.error(ADMIN_INVALID_ACCOUNT, ae.getMessage());
//            } catch (AuthenticationException ae) {
//                log.error("认证失败！", ae);
//                return ResultGenerator.error(ADMIN_INVALID_ACCOUNT, "认证失败！");
//            }
//            Subject subject = SecurityUtils.getSubject();
//            SecurityUtils.getSubject().getSession().setTimeout(sessionExpiredTime);
//            TbAdminUserPo user = (TbAdminUserPo) subject.getPrincipal();
//            user.setLastLoginIp(IpUtil.getIpAddr(request));
//            user.setLastLoginTime(LocalDateUtil.getNowLocalDateTime());
//            adminUserService.updateById(user);
//            log.info("用户" + loginUserDto.getUserName() + "登录系统！");
//            return ResultGenerator.success(subject.getSession().getId());
//        } catch (Exception e) {
//            log.error("登录接口异常：", e);
//            return ResultGenerator.error("登录失败！");
//        }
//    }

    @ApiOperation(value = "退出登录接口")
    @PostMapping("/logout")
    public Object logout() {
        try {
            redisUtil.del(String.format(RedisKeyConstant.ADMIN_USER_LOGIN_KEY, getAdminUserId()));
            return ResultGenerator.success();
        } catch (Exception e) {
            log.error("退出登录接口异常：", e);
            return ResultGenerator.error("退出登录失败");
        }
    }

    @ApiOperation(value = "个人信息接口")
    @RequiresAuthentication
    @GetMapping("/info")
    public Object info() {
        TbAdminUserPo adminUser = getCurrentUser();
        adminUser = adminUserService.getById(adminUser.getId());
        Map<String, Object> data = new HashMap<>();
        data.put("name", adminUser.getUserName());
        data.put("avatar", adminUser.getAvatar());
        Integer roleId = adminUser.getRoleId();
        Set<String> roles = adminRoleService.getSetRolesByIds(roleId);
        Set<String> permissions = adminPermissionService.getSetPermissionByRoleId(roleId);
        data.put("roles", roles);
        // NOTE
        // 这里需要转换perms结构，因为对于前端而言API形式的权限更容易理解
        data.put("perms", toAPI(permissions));
        return ResultGenerator.success(data);
    }

    @GetMapping("/401")
    public Object page401() {
        return "success";
    }

    @GetMapping("/index")
    public Object pageIndex() {
        return ResultGenerator.success();
    }

    @GetMapping("/403")
    public Object page403() {
        return ResultGenerator.error(506, "无操作权限");
    }

    private Collection<String> toAPI(Set<String> permissions) {
        if (systemPermissionsMap == null) {
            systemPermissionsMap = new HashMap<>();
            final String basicPackage = "com.cq.hd.admin";
            List<Permission> systemPermissions = PermissionUtil.listPermission(ApplicationContextHolder.getApplicationContext(), basicPackage);
            for (Permission permission : systemPermissions) {
                String perm = permission.getRequiresPermissions().value()[0];
                String api = permission.getApi();
                systemPermissionsMap.put(perm, api);
            }
        }
        Collection<String> apis = new HashSet<>();
        for (String perm : permissions) {
            String api = systemPermissionsMap.get(perm);
            apis.add(api);

            if (perm.equals("*")) {
                apis.clear();
                apis.add("*");
                return apis;
            }
        }
        return apis;
    }
}
